/**
 * @author: XuJinghui
 * @email: xujhui@foxmail.com
 * @date: 2021-5-22
 * @version: 1.0.0
 * @description: microflow为微型办公流程图插件，专注于OA系统流程图工具开发，纯js打造
 *
 * ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## #####
 * ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## #####
 *  __         __  _ __ _       _ __ __ _   __ __ _         __ __       _ __ __ __ _   __    		   __ __      __         __
 * |  \       /  ||_    _|    /  __ __ __| /   __  \       /      \    |   _ __ __ _| |  | 		     /      \    |  |       |  |
 * |   \     /   |  |  |     /  /          |  |  |  |     /   __   \   |  |           |  | 		    /   __   \   |  |       |  |
 * |    \   /    |  |  |    /  /           |  |  |  |    /   /  \   \  |  |_ __ __ _  |  | 		   /   /  \   \  |  |  ___  |  |
 * |     \_/     |  |  |   /  /            |  |__|  /    |  |    |   | |   _ __ __ _| |  | 		   |  |    |   | |  | /   \ |  |
 * |  |\     /|  |  |  |  |  |             |   __  \     |  |    |   | |  |           |  | 		   |  |    |   | |  |/     \|  |
 * |  | \___/ |  |  |  |   \  \            |  |  \  \    |  |    |   | |  |           |  | 		   |  |    |   | |     / \     |
 * |  |       |  |  |  |    \  \           |  |   \  \   \   \__/   /  |  |           |  | 		   \   \__/   /  |    /   \    |
 * |  |       |  | _|  |_    \  \_ __ _ _  |  |    \  \   \        /   |  |           |  |_ __ __ _ \        /   |   /     \   |
 * |__|       |__||_ __ _|    \ _ __ __ _| |__|     \__\   \ __ __/    |__|           |_ __ __ __ _| \ __ __/    |__/       \__|
 *  
 * ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## #####
 * ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## ########## #####
 */
(function() {
	
	/**
	 * 圆节点类型
	 */
	var CircleType = {
		start: 0,
		end: 1
	}
	
	/**
	 * 矩形类型
	 */
	var RectangleType = {
		circularize: 0,
		link: 1
	}
	
	/**
	 * 工具类型
	 */
	var toolType = {
		line: 0,
		scircle: 1,
		ecircle: 2,
		circularize: 3,
		link: 4,
		add: 5,
		del: 6,
		exp: 7,
		imp: 8,
		reset: 9
	}
	
	/**
	 * 矩形对象
	 * @param {String} id 唯一标识
	 * @param {String} name 名称
	 * @param {String} type 类型 环节|阅知（传阅）
	 * @param {Object} position 节点位置信息
	 * @param {Object} style 节点样式
	 */
	function Rectangle(args) {
		this.mf = args.mf; //关联流程
		this.id = args.id; //节点id(唯一标识)
		this.name = args.name; //名称
		this.type = args.type; //类型 环节|传阅
		this.position = Object.assign({ //位置
			x: null,
			y: null,
			h: null,
			w: null
		}, args.position);
		this.style = Object.assign({
			icon: this.type === RectangleType.link ? "images/person.png" : "images/circularize.png", //图标
			outline: null,//描边
			prevoutline: null,
			linewidth: null
		}, args.style);//样式
		this.style.prevoutline = this.style.outline;
	}

	/**
	 * 线对象
	 * @param {String} id 唯一标识
	 * @param {String} name 名称
	 * @param {LinkedList} position 节点位置信息
	 * @param {Node} prevNode 上一节点
	 * @param {Node} nextNode 下一节点
	 * @param {Object} style 节点样式
	 */
	function Line(args) {
		this.mf = args.mf; //关联流程
		this.id = args.id; //节点id(唯一标识)
		this.name = args.name; //名称
		this.position = args.position ? args.position : new LinkedList();
		this.prevNode = args.prevNode;//上一节点id
		this.nextNode = args.nextNode;//下一节点id
		this.style = Object.assign({
			outline: "black",//描边
			prevoutline: null,
			fill: "#55aaff",//填充
			lineWidth: "2"//粗细
		}, args.style);//样式
		this.style.prevoutline = this.style.outline;
	}
	
	/**
	 * 圆对象
	 * @param {String} id 唯一标识
	 * @param {String} name 名称
	 * @param {String} type 类型 开始|结束
	 * @param {Object} position 节点位置信息
	 * @param {Object} style 节点样式
	 */
	function Circle(args) {
		this.mf = args.mf; //关联流程
		this.id = args.id; //节点id(唯一标识)
		this.name = args.name; //名称
		this.type = args.type; //类型 开始|结束
		this.position = Object.assign({ //位置
			x: null,
			y: null,
			r: null
		}, args.position);
		this.style = Object.assign({
			icon: this.type === CircleType.start ? "images/start.png" : "images/end.png", //图标
			outline: null,//描边
			prevoutline: null,
			linewidth: null
		}, args.style);//样式
		this.style.prevoutline = this.style.outline;
	}

	/**
	 * 流程构造函数，用于创建流程对象
	 * @param {Object} name 流程名称
	 */
	var Microflow = function(cfg) {
		typeof cfg != "object" && (cfg = {});
		this.id = uuid(32, 16);//画布唯一标识
		this.nodes = [];//节点信息集
		this.lines = [];//线信息集
		this.config = Object.assign({//画布配置选项
			name: "MICROFLOW", //画布名称
			ele: null,//流程所在容器（值为css选择器）
			beforeCallback: null,//流程创建前的回调
			afterCallback: null,//流程创建后的回调
			icon: null,//节点图标
		}, cfg); //配置项
		this.name = this.config.name;//画布名称
	}

	/**
	 * 流程视图
	 * @param {Object} selector 选择器
	 * @param {Object} config cofig {title: "MicroFlow流程图工具", aside: "MicroFlow流程属性"}
	 */
	function MicroflowView(selector, config) {
		this.activenode = null;
		this.flows = [];//流程对象容器
		this.flows.push(new Microflow());
		this.currentflow = this.flows[0];
		var self = this;
		this.selector = selector;//选择器
		this.config = Object.assign({//视图配置
			title: "MicroFlow流程图工具", //视图标题
			aside: "MicroFlow流程属性"//视图右侧属性标题
		}, config);
		this.element = MFNode(selector);//视图MFNode对象
		this.id = uuid(32, 16);
		this.tools = {
			ele: null,
			selected: null,
			flag: null
		};//顶部工具栏
		this.statusConfig = {//画布状态信息
			IDLE: 0,//未拖动状态（静止状态）
			DRAG_START: 1,//是否可拖动状态（就绪状态）
			DRAGGING: 2//拖动中状态（运动状态）
		};
		this.canvasInfo = {//画布信息
			status: self.statusConfig.IDLE,
			drawTarget: null,
			lastEvtPos: {x: null, y: null},
			offsetEvtPos: {x: null, y: null}
		};		
		var createFlowHtml = function(){
			return '<div class="micro-container" ' + 'viewname="' + this.config.title + '"' + 'viewid="' + this.id + '">\
			<div class="title">'+ this.config.title +'</div>\
			<div class="micro-body">\
				<div class="micro-tools">\
					<div class="paint-tools">\
						<div micro-tools class="micro-tools-box"><div class="micro-square micro-square-link"><img src="images/person.png" width="30px" height="30px"/><span>环节</span></div></div>\
						<div micro-tools class="micro-tools-box"><div class="micro-square micro-square-circularize"><img src="images/circularize.png" width="30px" height="30px"/><span>传阅</span></div></div>\
						<div micro-tools class="micro-tools-box"><div class="micro-circle micro-circle-start"><img src="images/start.png" width="15px" height="15px"/></div></div>\
						<div micro-tools class="micro-tools-box"><div class="micro-circle micro-circle-end"><img src="images/end.png" width="15px" height="15px"/></div></div>\
						<div micro-tools class="micro-tools-box"><div class="micro-line"></div></div>\
					</div>\
					<div class="function-tools">\
						<input class="micro-inp-reset" value="# 清空" type="button" bindEvent="click=reset" />\
						<input class="micro-inp-exp" value="> 导出" type="button" bindEvent="click=exp" />\
						<input class="micro-inp-imp" value="< 导入" type="button" bindEvent="click=imp" />\
					</div>\
				</div>\
				<div class="micro-canvas beautify-scroll">\
					<canvas flowid="'+ this.flows[0].id +'" ></canvas>\
				</div>\
			</div>\
			<div class="micro-side">\
				<div class="micro-attribute-headline">microflow流程属性</div>\
				<div>\
					<input class="micro-display-none" mfid="id" name="id" type="text" />\
					<label>名称:</label>\
					<input mfid="name" name="name" type="text" />\
					<label>类型:</label>\
					<input mfid="type" name="type" type="text" readonly class="mf-readonly" />\
					<label>X坐标:</label>\
					<input mfid="xindex" name="xindex" type="text" />\
					<label>Y坐标:</label>\
					<input mfid="yindex" name="yindex" type="text" />\
					<label>宽度:</label>\
					<input mfid="width" name="width" type="number" />\
					<label>高度:</label>\
					<input mfid="height" name="height" type="number" />\
					<label>半径:</label>\
					<input mfid="radius" name="radius" type="number" />\
					<label>描边:</label>\
					<input mfid="outline" name="outline" type="text" />\
					<label>填充:</label>\
					<input mfid="fill" name="fill" type="text" />\
					<input class="micro-inp-attr-save" type="submit" value="保存属性" />\
					<input class="micro-inp-attr-del" type="button" value="删除属性" />\
				</div>\
			</div>\
		</div>'
		}
		//创建流程视图DOM
		this.element.html(createFlowHtml.apply(self));
		//绑定工具栏按钮点击事件
		this.tools.ele = MFNode("div[viewid='"+this.id+"'] div[micro-tools]").bind("click",function(){
			this.siblings().removeClass("micro-tools-box-click");
			if(this.hasClass("micro-tools-box-click")) {
				this.removeClass("micro-tools-box-click");
				self.tools.flag = null;
			} else {
				this.addClass("micro-tools-box-click");
				self.tools.selected = this;
				if(this.html().indexOf("link") > -1) {
					self.tools.flag = toolType.link;
				} else if(this.html().indexOf("micro-square-circularize") > -1) {
					self.tools.flag = toolType.circularize;
				} else if(this.html().indexOf("micro-circle-end") > -1) {
					self.tools.flag = toolType.ecircle;
				} else if(this.html().indexOf("micro-circle-start") > -1) {
					self.tools.flag = toolType.scircle;
				} else if(this.html().indexOf("micro-line") > -1) {
					self.tools.flag = toolType.line;
				}
			};
		});
		//属性栏mfnode对象
		this.property = MFNode("div[viewid='"+this.id+"'] div.micro-side");
		this.canvas = MFNode("canvas[flowid='"+ self.currentflow.id +"']");
		this.container = MFNode("div[viewid='"+this.id+"']");
		this.canvas.attr("width", this.canvas.parent().width() < 600 ? 600 : this.canvas.parent().width());
		this.canvas.attr("height", this.canvas.parent().height() * 2);
		var $brush = this.brush = this.canvas.ele[0].getContext("2d");
		var $width = this.canvas.width();
		var $height = this.canvas.height();
		//矩形计数器
		var $nodeCount = 0;
		//临时线
		var $tempLine = new LinkedList();
		var $fromNode = null;
		var $toNode = null;
		
		//设置背景
		setBackground($brush, $width, $height);
		//鼠标按下事件
		this.canvas.bind("mousedown", function(e) {
			var canvasPosition = getCanvasPosition(e);
			var ref = inChart(self.currentflow, canvasPosition);
			if(ref) {
				self.canvasInfo.drawTarget = ref.data;
				self.canvasInfo.status = self.statusConfig.DRAG_START;
				self.canvasInfo.lastEvtPos = canvasPosition;
				if(self.tools.flag == toolType.line) {
					$fromNode = ref.data;
					minP = getMinPosition($fromNode, canvasPosition);
					$tempLine.add([minP.x, minP.y]);
				}
			} else {
				active();
			}
		})
		
		//鼠标移动事件
		this.canvas.bind("mousemove", function(e) {
			var canvasPosition = getCanvasPosition(e);
			var d = inChart(self.currentflow, canvasPosition);
			if(d) {
				active(d.data);
				if(d.data instanceof Line) {
					self.canvas.css({"cursor": d.cursor});
				} else {
					if(self.tools.flag === toolType.line) {
						self.canvas.css({"cursor": "crosshair"});
					} else {
						self.canvas.css({"cursor": "move"});
					}
				}
			} else {
				self.canvas.css({"cursor": ""});
			}
			if(self.canvasInfo.status === self.statusConfig.DRAG_START && getDistance(canvasPosition, self.canvasInfo.lastEvtPos) > 5) {
				self.canvasInfo.status = self.statusConfig.DRAGGING;
			} else if(self.canvasInfo.status === self.statusConfig.DRAGGING) {
				if(self.tools.flag !== toolType.line) {
					var { drawTarget } = self.canvasInfo;
					var offsetPx = canvasPosition.x - self.canvasInfo.offsetEvtPos.x,
						offsetPy = canvasPosition.y - self.canvasInfo.offsetEvtPos.y;
					drawTarget.position.x += offsetPx;
					drawTarget.position.y += offsetPy;
					changeLinePositionByNode(self.currentflow, drawTarget, offsetPx, offsetPy);
				}
				
				//重新绘画
				renewpaint();
			}
			self.canvasInfo.offsetEvtPos = canvasPosition;
		})
		
		//鼠标抬起事件
		this.canvas.bind("mouseup", function(e) {
			self.canvasInfo.status = self.statusConfig.IDLE;
			if(self.tools.flag === toolType.line && $tempLine.size() > 0) {
				var nowp = getCanvasPosition(e);
				var t = inChart(self.currentflow, nowp);
				if(t && $fromNode !== ($toNode = t.data)) {					
					minP = getMinPosition($toNode, nowp);
					$tempLine.add([minP.x, minP.y]);
					var tline = new Line({
						"mf": self.currentflow, 
						"id": uuid(32, 16), 
						"prevNode": $fromNode,
						"nextNode": $toNode,
						"name": "线" + (self.currentflow.lines.length + 1),
						"position": $tempLine,
						"style": {"outline": "black"}
						})
					drawLine(self.brush, tline);
					self.currentflow.lines.push(tline);
					self.activenode = tline;
					var data = {
						"id": tline.id,
						"name": tline.name,
						"outline": tline.style.outline,
						"fill": tline.style.fill,
						"type": "带箭头线条",
						"xindex": getLineXYPostion("x", ListArray.toArray(tline.position)),
						"yindex": getLineXYPostion("y", ListArray.toArray(tline.position))
					}
					clearVal("div[viewid='"+self.id+"'] div.micro-side input");
					setVal("div[viewid='"+self.id+"'] div.micro-side", data);
				}
				$fromNode = $toNode  = t = nowp = data = null;
			}
			$tempLine = new LinkedList();
		})
		
		//绘画工具点击事件
		this.canvas.bind("click", function(e) {
			var ps = getCanvasPosition(e);
			var data,d,nodeline;
			if(self.tools.flag === toolType.scircle) {//开始节点
				nodeline = new Circle({
					"mf": self.currentflow,
					"id": uuid(32, 16), 
					"name": "开始", 
					"type": CircleType.start, 
					"position": {x: ps.x, y: ps.y, r: 20}, 
					"style": {"fill": "#42c862"}
					});
				drawNode(self.brush, nodeline);
				self.currentflow.nodes.push(nodeline);
				self.activenode = nodeline;
			} else if(self.tools.flag === toolType.ecircle) {//结束节点
				nodeline = new Circle({
					"mf": self.currentflow,
					"id": uuid(32, 16), 
					"name": "结束", 
					"type": CircleType.end,
					"position": {x: ps.x, y: ps.y, r: 20}, 
					"style": {"fill": "#ff1870"}
					});
				drawNode(self.brush, nodeline);
				self.currentflow.nodes.push(nodeline);
				self.activenode = nodeline;
			} else if(self.tools.flag === toolType.circularize || self.tools.flag === toolType.link) {
				nodeline = new Rectangle({
					"mf": self.currentflow, 
					"id": uuid(32, 16), 
					"name": "节点" + (++$nodeCount), 
					"type": self.tools.flag === toolType.circularize ? RectangleType.circularize : RectangleType.link, 
					"position": {x: ps.x, y: ps.y, w: 100, h: 40}, 
					"style": {"fill": "#55aaff"}
					});
				drawNode(self.brush, nodeline);
				self.currentflow.nodes.push(nodeline);
				self.activenode = nodeline;
			}
			if(d = inChart(self.currentflow, ps)) {
				d = d.data;
				self.activenode = d;
				if(d instanceof Circle) {
					data = {
						"id": d.id,
						"name": d.name,
						"xindex": d.position.x,
						"yindex": d.position.y,
						"radius": d.position.r,
						"outline": d.style.outline,
						"fill": d.style.fill,
						"type": d.type === CircleType.start ? "开始" : "结束"
					}
				} else if(d instanceof Rectangle) {
					data = {
						"id": d.id,
						"name": d.name,
						"width": d.position.w,
						"height": d.position.h,
						"xindex": d.position.x,
						"yindex": d.position.y,
						"outline": d.style.outline,
						"fill": d.style.fill,
						"type": d.type === RectangleType.circularize ? "传阅" : "环节"
					}
				} else if(d instanceof Line) {
					data = {
						"id": d.id,
						"name": d.name,
						"outline": d.style.outline,
						"fill": d.style.fill,
						"type": "带箭头线条",
						"xindex": getLineXYPostion("x", ListArray.toArray(d.position)),
						"yindex": getLineXYPostion("y", ListArray.toArray(d.position))
					}
				}
				self.activenode = d;
				clearVal("div[viewid='"+self.id+"'] div.micro-side input");
				setVal("div[viewid='"+self.id+"'] div.micro-side", data);
			}
		})
		
		/**
		 * 功能按钮事件绑定
		 */
		var functionbutton = MFNode("div[viewid='"+self.id+"'] input[bindEvent]");
		for(var i = 0, len = functionbutton.ele.length; i < len; i++) {
			var md = _transHtml2Dom(functionbutton.ele[i]);
			var be = md.attr("bindEvent");
			var evt = be.split("=")[0];
			var cb = be.split("=")[1];
			md.bind(evt, eval(cb));
		}
		
		/**
		 * 保存属性按钮事件
		 */
		this.property.find(".micro-inp-attr-save").bind("click", function() {
			var datas = getVal("div[viewid='"+self.id+"'] div.micro-side");
			if(!datas || !datas.id) {
				new Dialog({text: "无数据可保存！"});
				return false;
			}
			var nodeline = self.activenode
			nodeline.name = datas.name;
			datas.outline && (nodeline.style.outline = datas.outline);
			datas.fill && (nodeline.style.fill = datas.fill);
			if(nodeline instanceof Line) {
				if(datas.xindex && datas.yindex) {
					var arr = [];
					var x = datas.xindex.split(",");
					var y = datas.yindex.split(",");
					for(var i = 0, len = Math.min(x.length, y.length); i < len; i++) {
						arr[i] = [x[i], y[i]];
					}
					nodeline.position = ListArray.toList(arr);
				} 
			} else {
				if(nodeline instanceof Circle) {
					datas.radius && (nodeline.position.r = +datas.radius);
				}
				var offsetPx = datas.xindex - nodeline.position.x;
				var offsetPy = datas.yindex - nodeline.position.y;
				datas.xindex && (nodeline.position.x += offsetPx);
				datas.yindex && (nodeline.position.y += offsetPy);
				changeLinePositionByNode(self.currentflow, nodeline, offsetPx, offsetPy);
			}
			renewpaint();
			new Tip({text: "保存成功！"});
		})
		
		/**
		 * 删除属性按钮事件
		 */
		this.property.find(".micro-inp-attr-del").bind("click", function() {
			if(self.currentflow.nodes.length == 0 && self.currentflow.lines.length == 0) {
				new Dialog({text: "无数据可删除！"});
				return false;
			}
			new Dialog({text: "确认删除？", type: "confirm", confirmcallback: function() {
				var datas = getVal("div[viewid='"+self.id+"'] div.micro-side"),
					nodeid = datas.id,
					node = null,
					nodes = self.currentflow.nodes,
					lines = self.currentflow.lines,
					index;
				for(var i = 0, len = nodes.length; i < len; i++) {
					if(nodes[i].id == nodeid) {
						node = nodes[i];
						index = i;
						break;
					}
				}
				if(!node) {
					for(var i = 0, len = lines.length; i < len; i++) {
						if(lines[i].id == nodeid) {
							node = lines[i];
							index = i;
							break;
						}
					}
				}
				if(node instanceof Line) {
					lines.splice(index, 1);
				} else if(node instanceof Circle || node instanceof Rectangle) {
					var indexs = [];
					lines.forEach((e, i) => {
						if(e.prevNode === node || e.nextNode === node) {
							indexs.push(i);
						}
					})
					for(var j = 0, len = indexs.length; j < len; j++) {
						lines.splice(indexs[j] - j, 1);
					}
					nodes.splice(index, 1);
				}
				renewpaint();
				clearVal("div[viewid='"+self.id+"'] div.micro-side input[mfid]");
				new Tip({text: "删除成功！"});
				return true;
			}})	
		})
		
		/**
		 * 清空按钮事件
		 */
		function reset() {
			new Dialog({text: "确认清空？", type: "confirm", confirmcallback: function() {
				self.currentflow.lines = [];
				self.currentflow.nodes = [];
				renewpaint();
				clearVal("div[viewid='"+self.id+"'] div.micro-side input[mfid]");
				new Tip({text: "清空成功"});
				return true;
			}})
		}
		
		/**
		 * 导出流程
		 */
		function exp() {
			exportJsonFile(getWFdata(self.currentflow));
		}
		
		self.get = function() {
			return getWFdata(self.currentflow)
		};
		
		/**
		 * 导入流程
		 */
		function imp() {
			uploadFile(self);
		}
		
		self.set = function(data) {
			MicroflowView.set(data, self);
		};
		
		/**
		 * 重绘流程
		 */
		function renewpaint() {
			//清空画布
			$brush.clearRect(0, 0, $width, $height);
			setBackground($brush, $width, $height);
			self.currentflow.nodes.forEach(node => drawNode($brush, node));
			self.currentflow.lines.forEach(line => drawLine($brush, line));
		}
		
		/**
		 * 刷新流程图
		 */
		self.refresh = renewpaint;
		
		/**
		 * 使当前被选中或鼠标在其上面时节点或线更换样式
		 * @param {Object} node 被选中的节点
		 */
		function active(node) {
			self.currentflow.nodes.forEach((e, i) => {
				e.style.outline = e.style.prevoutline;
			})
			self.currentflow.lines.forEach((e, i) => {
				e.style.outline = e.style.prevoutline;
			})
			node && (node.style.outline = "#aa55ff") && (node.style.linewidth = 5);
			renewpaint();
		}
		
	}
	
	MicroflowView.set = function(data, mfv) {
		try{
			var wf = JSON.parse(data);
			var mf = new Microflow({name: wf.name});
			mf.id = wf.id;
			var nodes = [];
			var lines = [];
			wf.nodes.forEach((e, i) => {
				if(e.type === "link") {
					nodes.push(new Rectangle({
						"mf": mf,
						"id": e.id, 
						"name": e.name, 
						"type": RectangleType.link, 
						"position": e.position, 
						"style": {"fill": "#55aaff"}
					}))
				} else if(e.type === "circularize") {
					nodes.push(new Rectangle({
						"mf": mf,
						"id": e.id, 
						"name": e.name, 
						"type": RectangleType.circularize, 
						"position": e.position, 
						"style": {"fill": "#55aaff"}
					}))
				} else if(e.type === "start") {
					nodes.push(new Circle({
						"mf": mf,
						"id": e.id, 
						"name": e.name, 
						"type": CircleType.start, 
						"position": e.position, 
						"style": {"fill": "#42c862"}
					}))
				} else if(e.type === "end") {
					nodes.push(new Circle({
						"mf": mf,
						"id": e.id, 
						"name": e.name, 
						"type": CircleType.end, 
						"position": e.position, 
						"style": {"fill": "#ff1870"}
					}))
				}
			})
			
			wf.lines.forEach((e, i) => {
				lines.push(new Line({
					"mf": e.mfid,
					"id": e.id, 
					"prevNode": getNodeById(e.prevnodeid, nodes),
					"nextNode": getNodeById(e.nextnodeid, nodes),
					"name": e.name,
					"position": ListArray.toList(e.position),
					"style": {"outline": "black"}
				}))
			})
			
			mf.nodes = nodes;
			mf.lines = lines;
			mfv.currentflow = mf;
			mfv.flows.push(mf);
			mfv.refresh();
			new Tip({text: "导入成功！"});
		} catch(e) {
			new Dialog({"text": "文件类型或内容不符合格式要求，导入失败！", "type": "error"});
		} finally{
			wf = mf = nodes = lines = null;
		}
	}
	
	
	/**
	 * 获取流程数据
	 * @param {Object} mf 流程对象
	 */
	function getWFdata(mf) {
		var data = {};
		data.id = mf.id;
		data.name = mf.name;
		data.nodes = [];
		data.lines = [];
		mf.nodes.forEach((e, i) => {
			var n = {
				"mfid": mf.id,
				"id": e.id,
				"name": e.name,
				"position": e.position
			}
			if(e instanceof Rectangle) {
				if(e.type == 0) {
					n.type = "circularize";
				} else if(e.type == 1) {
					n.type = "link";
				}
			} else if(e instanceof Circle) {
				if(e.type == 0) {
					n.type = "start";
				} else if(e.type == 1) {
					n.type = "end";
				}
			}
			data.nodes.push(n);
		});
		mf.lines.forEach((e, i) => {
			var l = {
				"mfid": mf.id,
				"id": e.id,
				"name": e.name,
				"prevnodeid": e.prevNode ? e.prevNode.id : "",
				"nextnodeid": e.nextNode ? e.nextNode.id : "",
				"position": ListArray.toArray(e.position)
			}
			data.lines.push(l);
		});
		return data;
	}
	
	/**
	 * 导出Json文件
	 * @param {Object} data
	 * @param {Object} filename
	 */
	function exportJsonFile(data, filename) {
		if(!data) {
			new Tip({text: "数据为空，转换Json文件失败！"});
			return;
		}
		
		if(!filename) filename = "microflow.json";
		
		if(typeof(data) === "object") {
			data = JSON.stringify(data, undefined, 4);
		}
		
		var blob = new Blob([data], {type: "text/json"}),
			a = document.createElement("a");
		a.download = filename;
		
		a.href = window.URL.createObjectURL(blob);
		
		var evt = new MouseEvent("click", {});
		
		a.dispatchEvent(evt);
	}
	
	/**
	 * 将上传的josn文件转换为流程图
	 * @param {Object} mfv
	 */
	function uploadFile(mfv) {
		var inp = document.createElement("input");
		inp.accept = ".json"
		inp.type = "file";
		var evt = new MouseEvent("click", {});
		inp.dispatchEvent(evt);
		inp.addEventListener("change", (e) => {
			var file = inp.files ? inp.files[0] : null;
			if(file) {
				var reader = new FileReader();
				reader.readAsText(file, "UTF-8");
				reader.onload = function(evt) {
					var filestr = evt.target.result;
					MicroflowView.set(filestr, mfv);
				}
			}	
		})
	}
	
	function getNodeById(id, nodes) {
		for(var i = 0, len = nodes.length; i < len; i++) {
			if(nodes[i].id == id) {
				return nodes[i];
			}
		}
		return null;
	}
	
	/**
	 * 从line的位置数组中截取出x或y的所有值
	 * @param {Object} type x | y
	 * @param {Object} position 位置数组
	 */
	function getLineXYPostion(type, position) {
		var xp = [];
		type === "x" ? position.forEach((v, i) => {xp.push(v[0])}) : position.forEach((v, i) => {xp.push(v[1])})
		return xp.toString();
	}
	
	
	/**
	 * 设置特定元素下所有含mfid属性的子元素的值
	 * @param {Object} selector
	 */
	function setVal(selector, data) {
		var inps = MFNode(selector);
		if(inps.ele.length > 0) {
			inps = inps.find("input[mfid]");
			for(var i = 0, len = inps.ele.length; i < len; i++) {
				var d = _transHtml2Dom(inps.ele[i]);
				d.val(data[d.attr("mfid")]);
			}
		}
	}
	
	/**
	 * 获取特定元素下所有含mfid属性的子元素的值
	 * @param {Object} selector
	 */
	function getVal(selector) {
		var result = {};
		var inps = MFNode(selector);
		if(inps.ele.length > 0) {
			inps = inps.find("input[mfid]");
			for(var i = 0, len = inps.ele.length; i < len; i++) {
				result[inps.ele[i].getAttribute("mfid")] = inps.ele[i].value;
			}
		}
		return result;
	}
	
	/**
	 * 清空特定元素下所有含mfid属性的子元素的值
	 * @param {Object} selector
	 */
	function clearVal(selector) {
		var inps = MFNode(selector);
		for(var i = 0, len = inps.ele.length; i < len; i++) {
			var d = _transHtml2Dom(inps.ele[i]);
			d && d.attr("mfid") && d.val("");
		}
	}

	//将Microflow对象作为window的属性
	window.Microflow = Microflow;
	
	//已绑定的事件数组
	var events = [];

	/**
	 * 对DOM操作进行简单封装
	 */
	var MFNode = function(selector) {
		return _transHtml2Dom(getEle(selector));
	}
	
	/**
	 * dom操作方法封装
	 */
	var _dom = {
		ele : null,
		storage : null,
		data : function() {return _data(this, arguments)},
		find : function() {return _find(this, arguments)},
		val : function() {return _val(this, arguments);},
		html : function() {return _html(this, arguments);},
		text : function() {return _text(this, arguments);},
		css : function() {return _css(this, arguments);},
		attr : function() {return _attr(this, arguments);},
		bind : function(type, callback) {return _bind(this, arguments);},
		unbind : function(type, callback) {return _unbind(this, arguments);},
		hasClass : function(cname) {return _hasClass(this, cname);},
		addClass : function(cname) {return _addClass(this, cname);},
		removeClass : function(cname) {return _removeClass(this, cname);},
		show : function() {return _show(this)},
		hide : function() {return _hide(this)}, 
		width : function() {return _width(this, arguments)},
		height : function() {return _height(this, arguments)},
		parent : function() {return _parent(this)},
		siblings : function() {return _sibling(this.ele);},
		append : function(html) {return _append(this, html)}
	}
	
	/**
	 * 链式列表
	 */
	function LinkedList() {
		var first = null;
		var last = null;
		var size = 0;
		function Entry(item) {
			this.item = item;
			this.prev = null;
			this.next = null;
		}
		
		this.size = function() {
			return size;
		}
		
		this.add = function(item) {
			var entry = new Entry(item);
			if(last) {
				last.next = entry;
				entry.prev = last;
			} else {
				first = entry;
			}
			last = entry;
			size ++;
		}
		
		this.get = function(index, flag) {
			if(index > size - 1) {
				return null;
			}
			if(flag === true) {
				return node(index)
			} else {
				return node(index).item;
			}
		}
		
		function node(index) {
			if (index < (size >> 1)) {
				var x = first;
				for (var i = 0; i < index; i++)
					x = x.next;
				return x;
			} else {
				var x = last;
				for (var i = size - 1; i > index; i--)
					x = x.prev;
				return x;
			}
		}
		
		this.clear = function() {
			for (var x = first; x != null; ) {
				var next = x.next;
				x.item = null;
				x.next = null;
				x.prev = null;
				x = next;
			}
			first = last = null;
			size = 0;
		}
		
		this.first = function() {
			return first;
		}
		
		this.last = function() {
			return last;
		}
		
	}
	
	/**
	 * 列表-数组互转工具
	 */
	ListArray = {
		toArray : function(list) {
			var arr = [];
			for(var i = 0, len = list.size(); i < len ; i++) {
				arr.push(list.get(i));
			}
			return arr;
		},
		toList : function(array) {
			var list = new LinkedList();
			array.forEach((e, i) => list.add(e));
			return list;
		}
	}
	
	/**
	 * 会话框（用于提示信息）
	 * @param {Object} text | html
	 * @param {Object} type 会话框类型 [info,warn,error,confirm,form]
	 * @param {Object} selector
	 * @param {Object} title
	 * @param {Object} cancelcallback
	 * @param {Object} confirmcallback
	 * @param {Object} canceltext
	 * @param {Object} confirmtext
	 */
	function Dialog(args) {
		var self = this;
		self.flag = false;
		var cl = "";
		args = Object.assign({
			text: null,
			type: "info",
			selector: "body",
			title: null,
			cancelcallback: null,
			confirmcallback: null,
			canceltext: "取消",
			confirmtext: "确认"
		}, args);
		if(!args.title) {
			if(args.type === "info") {
				cl = "dialog-1-info";
				!args.title && (args.title = "提示信息");
			} else if(args.type === "warn") {
				cl = "dialog-1-warn";
				!args.title && (args.title = "警告信息");
			} else if(args.type === "error") {
				cl = "dialog-1-error";
				!args.title && (args.title = "错误信息");
			} else if(args.type === "confirm") {
				cl = "dialog-1-confirm";
				!args.title && (args.title = "确认信息");
			} else if(args.type === "form") {
				cl = "dialog-1-form";
				!args.title && (args.title = "表单信息");
			}
		}
		var id = uuid(32, 16),
			dialog1 = '<div mfdlid="'+ id +'" class="dialog-1 '+ cl +'">\
			<div class="dialog-1-header '+ cl +'">'+ args.title +'<span class="dialog-1-close">×<span></div>\
			<div class="dialog-1-body"></div>\
			<div class="dialog-1-footer"><input class="footer-cancel" type="button" value="'+ args.canceltext +'"/><input class="footer-confirm" type="button" value="'+ args.confirmtext +'"/></div>\
			</div>';
		MFNode(args.selector).append(dialog1);
		var header = MFNode('div[mfdlid="'+ id +'"] div.dialog-1-header'), x0 = null, y0 = null;
		var body = MFNode('div[mfdlid="'+ id +'"] div.dialog-1-body');
		var footer = MFNode('div[mfdlid="'+ id +'"] div.dialog-1-footer');
		var cancel = MFNode('div[mfdlid="'+ id +'"] div.dialog-1-footer input.footer-cancel');
		var confirm = MFNode('div[mfdlid="'+ id +'"] div.dialog-1-footer input.footer-confirm');
		if(args.type === "confirm" || args.type === "form") {
			cancel.bind("click", function() {
				if(args.cancelcallback) {
					args.cancelcallback.call(this, getVal('div[mfdlid="'+ id +'"] div.dialog-1-body')) && shutdown();
				} else {
					shutdown();
				}				
			});
			confirm.bind("click", function() {
				if(args.confirmcallback) {
					args.confirmcallback.call(this, getVal('div[mfdlid="'+ id +'"] div.dialog-1-body')) && shutdown();
				} else {
					shutdown();
				}	
			})
		} else {
			footer.hide();
		}
		body.html(args.text);
		header.bind("mousedown", function(e) {
			self.flag = true;
			x0 = e.offsetX;
			y0 = e.offsetY;
		});
		header.bind("mousemove", function(e) {
			var x1 = e.offsetX, y1 = e.offsetY;
			if(self.flag == true) {
				var boxp = header.parent();
				boxp.css({"top": y1 - y0 + +boxp.css("top").replace("px", "") + "px", "left": x1 - x0 + +boxp.css("left").replace("px", "") + "px"});
			}
		});
		header.bind("mouseup", function(e) {
			self.flag = false;
		});
		var close = MFNode('div[mfdlid="'+ id +'"] div.dialog-1-header span.dialog-1-close');
		close.bind("click", function(e) {
			shutdown();
		});
		function shutdown() {
			MFNode('div[mfdlid="'+ id +'"]').ele[0].remove();
		}
	}
	
	/**
	 * 提示信息弹框
	 * args = {
	 *	 text: "",提示信息
	 *   type: "topcenter",弹框类型，topleft | topright | bottomleft | bottomright | topcenter(默认值)
	 *   timeout: 3000 自动关闭时间，单位毫秒，值为0表示不自动关闭，默认值为3000毫秒
	 * }
	 * @param {Object} args
	 */
	function Tip(args){
		var args = Object.assign({
			timeout: 3000,
			type: "topcenter"
		}, args);
		var id = uuid(32, 16);
		var tipHtml = '<div mftid="'+ id +'" class="mf-tip mf-tip-'+ args.type +'">'+ args.text +'<span class="mf-tip-close">×</span></div>';
		MFNode("body").append(tipHtml);
		var closeEle = MFNode('div[mftid="'+ id +'"] span.mf-tip-close');
		closeEle.bind("click", function(e) {
			shutdown();
		});
		var shutdown = function() {
			MFNode('div[mftid="'+ id +'"]').ele[0].remove();
		}
		setTimeout(shutdown, args.timeout);
	}
	
	/**
	 * uuid生成器
	 * @param {Object} len 长度
	 * @param {Object} radix 数制
	 */
	function uuid(len, radix) {
		var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
		var uuid = [], i;
		radix = radix || chars.length;
		if(len) {
			for(i = 0; i < len; i++) {
				uuid[i] = chars[0 | Math.random() * radix];
			}
		} else {
			var r;
			uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
			uuid[14] = "4";
			for(i = 0; i < 36; i++) {
				if(!uuid[i]) {
					r = 0 | Math.random() * 16;
					uuid[i] = chars[i == 9 ? (r & 0x3) | 0x8 : r];
				}
			}
		}
		return uuid.join("");
	}
	
	/**
	 * 结果数据封装
	 * @param {Object} data
	 * @param {String} cursor 光标样式 {可选}
	 * @param {String} msg 信息 {可选}
	 */
	function Result(data, cursor, msg) {
		this.data = data;
		this.msg = msg || "";
		this.cursor = cursor || "";
	}
	
	/**
	 * 获取event事件当前位置坐标
	 * @param {Object} e
	 */
	function getCanvasPosition(e) {
		return {
			"x": e.offsetX,
			"y": e.offsetY
		}
	}
	
	/**
	 * 获取ps中距t最近的值（坐标）
	 * @param {Object} t 目标
	 * @param {Object} ps 比较值（不定长参数）
	 */
	function getMinPositionToTarget(t, ps) {
		if(arguments.length >= 2) {
			var obj = {};
			var arr = [];
			var d;
			for(var i = 1; i < arguments.length; i++) {
				d = getDistance(t, arguments[i]);
				obj[d] = arguments[i];
				arr.push(d);
			}
			return obj[Math.min(...arr)];
		}
	}
	
	/**
	 * 获取当前位置与当前节点（矩形、圆）四个边中心位置的最小距离
	 * @param {Object} node
	 * @param {Object} p
	 */
	function getMinPosition(node, p) {
		var tp,top,right,bottom,left,mimP;
		tp = node.position;
		if(node instanceof Rectangle) {
			top = {x: tp.x  + tp.w / 2, y: tp.y};
			right = {x: tp.x + tp.w, y: tp.h / 2 + tp.y};
			bottom = {x: tp.x + tp.w / 2, y: tp.h + tp.y};
			left = {x: tp.x, y: tp.h / 2 + tp.y};
		} else if(node instanceof Circle) {					
			top = {x: tp.x, y: tp.y - tp.r};
			right = {x: tp.x + tp.r, y: tp.y};
			bottom = {x: tp.x, y: tp.r + tp.y};
			left = {x: tp.x - tp.r, y: tp.y};					
		}
		return getMinPositionToTarget(p, top, right, bottom, left);
	}
	
	/**
	 * 节点移动时改变与其关联的线的开始位置坐标或结束位置坐标
	 * @param {Object} mf
	 * @param {Object} node
	 * @param {Object} offsetX
	 * @param {Object} offsetY
	 */
	function changeLinePositionByNode(mf, node, offsetX, offsetY) {
		mf.lines.forEach((e, i) => {
			var f, x, y;
			if(e.prevNode === node) {
				f = e.position.first().item;
				x = f[0] + offsetX;
				y = f[1] + offsetY;
				f.length = 0;
				f.push(x, y);
			} else if(e.nextNode === node) {
				f = e.position.last().item;
				x = f[0] + offsetX;
				y = f[1] + offsetY;
				f.length = 0;
				f.push(x, y);
			}
		})
	}
	
	/**
	 * 目标位置是否在画布画图内
	 * @param {Microflow} mf 画布对象
	 * @param {Object} pos 目标位置 {x: 9, y: 9}
	 */
	function inChart(mf, pos) {
		var ps = null;
		var nodes = mf.nodes;
		var lines = mf.lines;
		
		for(var i = 0, len = nodes.length; i < len; i++) {
			ps = nodes[i].position;
			if(nodes[i] instanceof Circle) {
				if(inCircle(ps, ps.r, pos)) return new Result(nodes[i]);
			} else if(nodes[i] instanceof Rectangle) {
				if(inRectangle(ps, ps.w, ps.h, pos)) return new Result(nodes[i]);
			}
		}
		
		for(var j = 0, len2 = lines.length; j < len2; j++) {
			ps = ListArray.toArray(lines[j].position);
			for(var m = 0, len3 = ps.length; m < len3; m++) {
				if(inCircle({x: ps[m][0], y: ps[m][1]}, 5, pos)) return new Result(lines[j], "crosshair");
			}
			if(nearLine(ps, pos, 3)) return new Result(lines[j], "w-resize");
		}
		return false;
	}
	
	/**
	 * 目标位置是否在圆内
	 * @param {Object} o 圆心坐标 {x: 9, y: 9}
	 * @param {Number} r 半径
	 * @param {Object} ps 目标位置 {x: 9, y: 9}
	 */
	function inCircle(o, r, ps) {
		return getDistance(o, ps) < r;
	}
	
	/**
	 * 目标位置是否在矩形内
	 * @param {Object} ltc 圆心坐标 {x: 9, y: 9}
	 * @param {Number} w 矩形宽度
	 * @param {Number} h 矩形高度
	 * @param {Object} ps 目标位置 {x: 9, y: 9}
	 */
	function inRectangle(ltc, w, h, ps) {
		return ps.x >= ltc.x && ps.x <= ltc.x + w && ps.y >= ltc.y && ps.y <= ltc.y + h
	}
	
	/**
	 * 目标位置是否在线内或距线r距离内
	 * @param {Array} cs 线节点坐标二维数组 [[0, 2], [3, 5], [6, 8]]
	 * @param {Object} ps 目标位置 {x: 9, y: 9}
	 * @param {Number} r 离线距离 {可选}
	 */
	function nearLine(cs, ps, r) {
		!r && (r = 0);
		for(var m = 0, len = cs.length - 1; m < len; m++) {
			var k = (cs[m][1] - cs[m + 1][1]) / (cs[m][0] - cs[m + 1][0]);//斜率
			var b = - ((cs[m + 1][0] * cs[m][1] - cs[m][0] * cs[m + 1][1]) / (cs[m + 1][0] - cs[m][0]));
			var minY = Math.min(cs[m][1], cs[m + 1][1]);
			var maxY = Math.max(cs[m][1], cs[m + 1][1]);
			var minX = Math.min(cs[m][0], cs[m + 1][0]);
			var maxX = Math.max(cs[m][0], cs[m + 1][0]);
			if(cs[m][1] == cs[m + 1][1]) {
				if(inSection(minX - r, maxX + r, ps.x) && inSection(minY - r, minY + r, ps.y)) {
					return true;
				}
			}
			if(cs[m][0] == cs[m + 1][0]) {
				if(inSection(minY - r, maxY + r, ps.y) && inSection(minX - r, minX + r, ps.x)) {
					return true;
				}
			}
			if(inSection(ps.y - r, ps.y + r, k * ps.x + b) && ps.y >= minY && ps.y <= maxY && ps.x >= minX && ps.x <= maxX) {
				return true;
			}
		}
		return false;
	}
	
	//判断是否在某个区间内
	function inSection(start, end, arg) {
		if(arg >= start && arg <= end) return true;
		return false;
	}
	
	//获取两点距离
	function getDistance(p1, p2) {
		return Math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2);
	}
	
	/**
	 * 设置画布背景
	 * @param {Object} el
	 */
	function setBackground(brush, width, height) {
		for(var i = 0, len = width / 10; i <= len; i++) {
			drawLine(brush, [[10 + 10 * i, 0], [10 + 10 * i, height]], i % 5 == 0 ? {outline: "#ffa9ff"} : {outline: "#e1f7ff"});
		}
		for(var j = 0, len2 = height / 10; j <= len2; j++) {
			drawLine(brush, [[0, 10 + 10 * j], [width, 10 + 10 * j]], j % 5 == 0 ? {outline: "#ffa9ff"} : {outline: "#e1f7ff"});
		}
	}
	
	/**
	 * 绘画节点
	 * @param {Object} brush
	 * @param {Object} node
	 */
	function drawNode(brush, node) {
		var width = 0;
		brush.font="20px Arial";
		if(node instanceof Circle) {
			drawCircle(brush, node.position.x, node.position.y, node.position.r, node.style);
			drawImage(brush, node);
		} else if(node instanceof Rectangle) {
			width = brush.measureText(node.name).width + 55;
			var offsetPx = (width - node.position.w) / 2;
			node.position.w = width;
			changeLinePositionByNode(node.mf, node, offsetPx, 0);
			drawRect(brush, node.position.x, node.position.y, node.position.w, node.position.h, node.style);
			brush.fillStyle = "white";
			//绘画节点名称
			brush.fillText(node.name, node.position.x + 35, (node.position.h) / 2 + node.position.y + 7);
			drawImage(brush, node);
		} else {}
	}
	
	/**
	 * 绘画线
	 * @param {Object} brush  画笔
	 * @param {Object} arg0 line | postion
	 * @param {Object} arg1 style
	 */
	function drawLine(brush, arg0, arg1) {
		var ps, style;
		if(arg0 instanceof Line) {
			ps = ListArray.toArray(arg0.position);
			style = arg0.style;
		} else {
			ps = arg0;
			style = arg1;
		}
		brush.save(); 
		brush.beginPath();
		brush.moveTo(+ps[0][0], +ps[0][1]);
		//画主线
		for(var i = 1, len1 = ps.length; i < len1; i++) {
			brush.lineTo(+ps[i][0], +ps[i][1]);
		}
		style && style.lineWidth && (brush.lineWidth = style.lineWidth.replace(/\D/g, ""));
		style && style.outline && (brush.strokeStyle = style.outline) || (brush.strokeStyle = "#526444");
		brush.stroke();
		brush.closePath();
		brush.restore();
		//画箭头
		if(arg0 instanceof Line) {
			var len = ps.length;
			var sp = ps[ps.length - 2];
			var ep = ps[ps.length - 1];
			drawArrow(brush, sp[0], sp[1], ep[0], ep[1], 30, 10, style.lineWidth, style.outline);
		}
	}
	
	/**
	 * 绘画箭头
	 * @param {Object} ctx
	 * @param {Object} fromX
	 * @param {Object} fromY
	 * @param {Object} toX
	 * @param {Object} toY
	 * @param {Object} theta
	 * @param {Object} headlen
	 * @param {Object} width
	 * @param {Object} color
	 */
	function drawArrow(ctx, fromX, fromY, toX, toY, theta, headlen, width, color) {
		fromX = +fromX;
		fromY = +fromY;
		toX = +toX;
		toY = +toY;
	 
	    theta = typeof(theta) != 'undefined' ? theta : 30;
	    headlen = typeof(theta) != 'undefined' ? headlen : 10;
	    width = typeof(width) != 'undefined' ? width : 1;
	    color = typeof(color) != 'undefined' ? color : '#000';
	 
	    var angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI,
	        angle1 = (angle + theta) * Math.PI / 180,
	        angle2 = (angle - theta) * Math.PI / 180,
	        topX = headlen * Math.cos(angle1),
	        topY = headlen * Math.sin(angle1),
	        botX = headlen * Math.cos(angle2),
	        botY = headlen * Math.sin(angle2);
	 
	    ctx.save();
	    ctx.beginPath();
	 
	    var arrowX = fromX - topX,
	        arrowY = fromY - topY;
	 
	    ctx.moveTo(arrowX, arrowY);
	    arrowX = toX + topX;
	    arrowY = toY + topY;
	    ctx.moveTo(arrowX, arrowY);
	    ctx.lineTo(toX, toY);
	    arrowX = toX + botX;
	    arrowY = toY + botY;
	    ctx.lineTo(arrowX, arrowY);
	    ctx.strokeStyle = color;
		ctx.fillStyle = color;
	    ctx.lineWidth = width;
	    ctx.fill();
	    ctx.restore();
	}
	
	/**
	 * 绘画圆
	 * @param {Object} brush 画笔
	 * @param {Object} x x坐标
	 * @param {Object} y y坐标
	 * @param {Object} r 半径
	 */
	function drawCircle(brush, x, y, r, style) {
		brush.save();
		brush.beginPath();
		style.linewidth && (brush.lineWidth = style.linewidth);
		brush.arc(x, y, r, 0, Math.PI * 2);
		style && style.outline && (brush.strokeStyle = style.outline) && brush.stroke();
		style && style.fill && (brush.fillStyle = style.fill) && brush.fill();
		brush.closePath();
		brush.restore();
	}
	
	/**
	 * 绘画矩形
	 * @param {Object} brush
	 * @param {Object} x
	 * @param {Object} y
	 * @param {Object} w
	 * @param {Object} h
	 * @param {Object} style
	 */
	function drawRect(brush, x, y, w, h, style) {
		brush.save();
		brush.beginPath();
		style.linewidth && (brush.lineWidth = style.linewidth);
		brush.rect(x, y, w, h);
		style && style.outline && (brush.strokeStyle = style.outline) && brush.stroke();
		style && style.fill && (brush.fillStyle = style.fill) && brush.fill();
		brush.closePath();
		brush.restore();
	}
	
	/**
	 * 绘画图像
	 * @param {Object} brush
	 * @param {Object} node
	 */
	function drawImage(brush, node) {
		brush.save();
		brush.beginPath();
		var img = new Image(32, 32);
		img.src = node.style.icon;
		if(node instanceof Circle) {
			brush.drawImage(img, node.position.x - 8, node.position.y - 8);
		} else if(node instanceof Rectangle) {
			brush.drawImage(img, node.position.x, (node.position.h - 32) /2 + node.position.y);
		} else {}
		brush.closePath();
		brush.restore();
	}
	
	/**
	 * 深克隆dom对象
	 * @param {Object} domEle
	 */
	function _transHtml2Dom(domEle){
		var newdom = Object.assign({}, _dom);
		newdom.ele = domEle;
		return newdom;
	};
	
	function _parent(self) {
		if(self.ele instanceof Array) {
			var arr = [];
			for(var i = 0, len = self.ele.length; i < len; i++) {
				arr.push(self.ele[i].parentNode);
			}
			return _transHtml2Dom(arr);
		}
		return _transHtml2Dom(self.ele.parentNode);
	}
	
	function _width(self, args) {
		if(args.length > 0) {
			if(self.ele instanceof Array) {
				for(var i = 0, len = self.ele.length; i < len; i++) {
					self.ele[i].style.width = ("" + args[0]).replace(/\D/g, "") + "px";
				}
			} else {
				self.ele.style.width = ("" + args[0]).replace(/\D/g, "") + "px";
			}
		} else {
			if(self.ele instanceof Array && self.ele.length > 0) {
				return self.ele[0].clientWidth - getStyle(self.ele[0], "paddingLeft").replace(/\s+|px/gi, "") - getStyle(self.ele[0], "paddingRight").replace(/\s+|px/gi, "");
			} else {
				return self.ele.clientWidth - getStyle(self.ele, "paddingLeft").replace(/\s+|px/gi, "") - getStyle(self.ele, "paddingRight").replace(/\s+|px/gi, "");
			}
		}
		return self;
	}
	
	/**
	 * 获取元素样式的值
	 * @param {Object} ele
	 * @param {Object} attr
	 */
	function getStyle(ele, attr) {
		return ele.currentStyle ? ele.currentStyle[attr] : getComputedStyle(ele, false)[attr];
	}
	
	function _height(self, args) {
		if(args.length > 0) {
			if(self.ele instanceof Array) {
				for(var i = 0, len = self.ele.length; i < len; i++) {
					self.ele[i].style.height = ("" + args[0]).replace(/\D/g, "") + "px";
				}
			} else {
				self.ele.style.height = ("" + args[0]).replace(/\D/g, "") + "px";
			}
		} else {
			if(self.ele instanceof Array && self.ele.length > 0) {
				return self.ele[0].clientHeight - getStyle(self.ele[0], "paddingTop").replace(/\s+|px/gi, "") - getStyle(self.ele[0], "paddingBottom").replace(/\s+|px/gi, "");
			} else {
				return self.ele.clientHeight - getStyle(self.ele, "paddingTop").replace(/\s+|px/gi, "") - getStyle(self.ele, "paddingBottom").replace(/\s+|px/gi, "");
			}
		}
		return self;
	}
	
	function _find(self, args) {
		var result = [];
		if(args.length > 0) {
			if(self.ele.length > 0) {
				var childs = MFNode(args[0]);
				for(var i = 0, len = childs.ele.length; i < len; i++) {
					for(var j = 0, len2 = self.ele.length; j < len; j++) {
						if(findParent(childs.ele[i], self.ele[j])) {
							result.push(childs.ele[i]);
							break;
						}
					}
				}
			}
		} 
		return _transHtml2Dom(result);
	}
	
	function findParent(node, parentnode) {
		if(node && parentnode) {
			if(node.parentNode == parentnode) {
				return node;
			} else {
				return findParent(node.parentNode, parentnode);
			}			
		} else {
			return null;
		}
	}
	
	function _data(self, args) {
		if(args.length > 0) {
			self.storage = args[0];
		} else {
			return self.storage;
		}
		return self;
	}
	
	function _show(self) {
		return _removeClass(self, "micro-display-none");
	}
	
	function _hide(self) {
		return _addClass(self, "micro-display-none");
	}
	
	function _sibling(ele) {
		var self = ele instanceof Array && ele.length > 0 ? ele[0] : ele;
		var siblings = self.parentNode.childNodes;
		var result = [];
		for(var i = 0; i < siblings.length; i++) {
			if(siblings[i].nodeType == 1 && siblings[i] != self) {
				result.push(siblings[i]);
			}
		}
		return _transHtml2Dom(result);
	}
	
	function _append(self, html) {
		html = html.trim();
		var left = html.indexOf(">"),
			right = html.lastIndexOf("<");
			tag = html.substring(right + 2, html.length - 1),
			attrs = str2attrarr(html.substring(tag.length + 1, left));
		var domnode = null;
		if(self.ele instanceof Array) {
			for(var i = 0, len = self.ele.length; i < len; i++) {
				domnode = document.createElement(tag);
				self.ele[i].appendChild(domnode);
				var mfdom = _transHtml2Dom(domnode).html(html.substring(left + 1, right));
				for(var j = 0, len2 = attrs.length; j < len2; j++) {
					mfdom.attr(attrs[j].k, attrs[j].v);
				}
			}
		} else {
			domnode = document.createElement(tag);
			self.ele.appendChild(domnode);
			var mfdom = _transHtml2Dom(domnode).html(html.substring(left + 1, right));
			for(var j = 0, len2 = attrs.length; j < len2; j++) {
				mfdom.attr(attrs[j].k, attrs[j].v);
			}
		}
		return self;
	}
	
	function _removeClass(self, cname) {
		if (self.ele instanceof Array) {
			for (var i in self.ele) {
				popClass(self.ele[i], cname);
			}
		} else {
			popClass(self.ele, cname);
		}
	}
	
	function _addClass(self, cname) {
		if (self.ele instanceof Array) {
			for (var i in self.ele) {
				pushClass(self.ele[i], cname);
			}
		} else {
			pushClass(self.ele, cname);
		}
	}
	
	function _hasClass(self, cname) {
		return self.ele instanceof Array && self.ele.length > 0 ? existClass(self.ele[0], cname) : existClass(self.ele, cname);
	}
	
	function _unbind(self, args) {
		if (args.length == 1) {
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					var el = findEleBindEvent(events, self.ele[i], args[0]);
					if (el) {
						self.ele[i].removeEventListener(args[0], el["ele"].method);
						events.splice(el["index"], 1);
					}
				}
			} else {
				var el = findEleBindEvent(events, self.ele, args[0]);
				if (el) {
					self.ele.removeEventListener(args[0], el["ele"].method);
					events.splice(el["index"], 1);
				}
			}
		} else if (args.length == 2) {
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					self.ele[i].removeEventListener(args[0], args[1]);
					var el = findEleBindEvent(events, self.ele[i], args[0]);
					if (el) {
						events.splice(el["index"], 1);
					}
				}
			} else {
				self.ele.removeEventListener(args[0], args[1]);
				var el = findEleBindEvent(events, self.ele, args[0]);
				if (el) {
					events.splice(el["index"], 1);
				}
			}
		} else {}
		return self;
	}
	
	function _bind(self, args) {
		if (self.ele instanceof Array) {
			for (var i in self.ele) {
				self.ele[i].addEventListener(args[0], function(e) {
					return args[1].call(_transHtml2Dom(this), e, this);
				});
				events.push({
					ele: self.ele[i],
					type: args[0],
					method: args[1]
				});
			}
		} else {
			self.ele.addEventListener(args[0], function(e) {
				return args[1].call(_transHtml2Dom(this), e, this);
			});
			events.push({
				ele: self.ele,
				type: args[0],
				method: args[1]
			});
		}
		return self;
	}
	
	function _attr(self, args) {
		if (args.length == 1) { //取值
			if (self.ele instanceof Array && self.ele.length > 0) {
				return self.ele[0].getAttribute(args[0]);
			}
			return self.ele.getAttribute(args[0]);
		} else if (args.length == 2) { //赋值
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					self.ele[i].setAttribute(args[0], args[1]);
				}
			} else {
				self.ele.setAttribute(args[0], args[1]);
			}
		} else {} //其他参数个数，不予处理
		return self;
	}
	
	function _css(self, args) {
		if (args.length == 1) { //批量赋值
			if(typeof args[0] == "object") {
				var arg0 = args[0];
				if (self.ele instanceof Array) {
					for (var i in self.ele) {
						if (typeof arg0 == "object") {
							for (var k in arg0) {
								self.ele[i].style.setProperty(k, arg0[k]);
							}
						}
					}
				} else {
					if (typeof arg0 == "object") {
						for (var k in arg0) {
							self.ele.style.setProperty(k, arg0[k]);
						}
					}
				}
			} else {//取值
				return self.ele instanceof Array ? getStyle(self.ele[0], args[0]) : getStyle(self.ele, args[0]);
			}			
		} else if (args.length == 2) { //单个赋值
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					self.ele[i].style.setProperty(args[0], args[1]);
				}
			} else {
				self.ele.style.setProperty(args[0], args[1]);
			}
		} else {} //其他参数个数，不予处理
		return self;
	}
	
	function _text(self, args) {
		if (args.length > 0) { //赋值
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					self.ele[i].innerText = args[0] || "";
				}
			} else {
				self.ele.innerText = args[0] || "";
			}
			return self;
		} else { //取值
			if (self.ele instanceof Array && self.ele.length > 0) {
				return self.ele[0].innerText;
			}
			return self.ele.innerText;
		}
	}
	
	function _html(self, args) {
		if (args.length > 0) { //赋值
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					self.ele[i].innerHTML = args[0] || "";
				}
			} else {
				self.ele.innerHTML = args[0] || "";
			}
			return self;
		} else { //取值
			if (self.ele instanceof Array && self.ele.length > 0) {
				return self.ele[0].innerHTML;
			}
			return self.ele.innerHTML;
		}
	}
	
	function _val(self, args) {
		if (args.length > 0) { //给所有符合条件的赋值
			if (self.ele instanceof Array) {
				for (var i in self.ele) {
					self.ele[i].value = args[0] || "";
				}
			} else {
				self.ele.value = args[0] || "";
			}
			return self;
		} else { //取符合条件的第一个的值
			if (self.ele instanceof Array) {
				return self.ele[0].value;
			}
			return self.ele.value;
		}
	}
	
	/**
	 * 将attr字符串提取为attr数组
	 * @param {Object} str 如'id="aa" class="bbb ccc"'
	 */
	function str2attrarr(str) {
		str = str.trim();
		var result = [],
			str1 = "",
			k = "",
			v = "";
		while(str) {
			k = str.match(/^[a-zA-Z\d-_]*/) && str.match(/^[a-zA-Z\d]*/)[0];
			str = str.substring(k.length, str.length).trim();
			if(str.charAt(0) == "=") {
				str = str.substring(1, str.length).trim();
				v = str.match(/^"{1}[a-zA-Z\d\s-_]*"{1}/) && str.match(/^"{1}[a-zA-Z\d\s-_]*"{1}/)[0].replace(/"/g, "");
				str = str.substring(v ? v.length + 2 : 0, str.length).trim();
			}
			result.push({"k": k, "v": v});
			k = v = "";
		}
		return result;		
	}
	
	function existClass($e, cname) {
		var cl = $e.getAttribute("class");
		var cls = cl ? cl.split(" ") : null;
		if(cls) {
			for(var i = 0, len = cls.length; i < len; i++) {
				if(cls[i] === cname) return true;
			}
		}
		return false;
	}
	
	function popClass($e, cname) {
		var cl;
		if((cl = $e.getAttribute("class")) == null) return;
		if(cl.indexOf(cname)) {
			cl = cl.replace(cname,"").trim();
		}
		$e.setAttribute("class", cl);
	}
	
	function pushClass($e, cname) {
		$e.getAttribute("class") == null ? $e.setAttribute("class") : null;
		var cl = $e.getAttribute("class");
		cl += " " + cname;
		$e.setAttribute("class", cl);
	}
	
	/**
	 * 找出通过MFNode.bind方法绑定的事件
	 * @param {Object} arr MFNode.bind绑定的事件数组
	 * @param {Object} ele 目标元素
	 * @param {Object} type 时间类型
	 */
	function findEleBindEvent(arr, ele, type) {
		for (var i = 0, len = arr.length; i < len; i++) {
			if (arr[i].ele == ele && arr[i].type == type) return {
				ele: arr[i],
				index: i
			};
		}
		return null;
	}

	/**
	 * 选择器
	 * @param {Object} selector 选择器参数
	 */
	function getEle(selector) {
		var result = [];
		var ds = document.querySelectorAll(selector)
		for(var i = 0, len = ds.length; i < len; i++) {
			result.push(ds[i]);
		}
		return result;
	}
	
	/**
	 * documnet加载事件
	 * @param {Object} callback 回调函数
	 */
	function domcumentReady(callback) {
		document.addEventListener('DOMContentLoaded', function() {
			//注销时间，避免重复触发
			document.removeEventListener('DOMContentLoaded', arguments.callee, false);
			callback && callback();
		})
	}
	
	//将microflow的默认documnet加载事件作为window的属性
	MFNode.ready = domcumentReady;
	
	//将dom操作工具作为window的属性
	window.$mf = window.mfquery = MFNode;
	
	//将Dialog绘画对象作为window的属性
	window.Dialog = Dialog;
	
	//将MicroflowView绑到window对象上
	window.MicroflowView = MicroflowView;
	
})();
